
package edu.webbapps.cormac_susilo.auth;

import edu.webbapps.cormac_susilo.bootstrap.BootWrap;
import edu.webbapps.cormac_susilo.core.User;
import java.io.IOException;
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.map.ObjectMapper;

/**
 * Servlet for Google Sign in
 * 
 * Handles both initial request from the user
 * as well as the redirected request form Google
 * @author Joel
 *
 */
@WebServlet(name = "oauth", urlPatterns = {"/oauth"})
public class GoogleServlet extends HttpServlet {

    @Inject
    BootWrap cormac;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");


        //Helper class for google operations
        final GoogleAuthHelper helper = new GoogleAuthHelper();
        HttpSession session = request.getSession(false);


        // case 1: first visit to page,  generate URI and redirect to Google
        if (request.getParameter("code") == null
                || request.getParameter("state") == null || session == null) {

            String url = helper.buildLoginUrl();

            // save state token in session, so we can later authenticate the response
            session.setAttribute("state", helper.getStateToken());

            //redirect to Google Login
            response.sendRedirect(url);

            return;

        } // Case 2: User is redirected from google with an access token
        else if (request.getParameter("code") != null && request.getParameter("state") != null
                && request.getParameter("state").equals(session.getAttribute("state"))) {

            session.removeAttribute("state");

            //parse the Json object returned from google using Jackson
            String googleJson = helper.getUserInfoJson(request.getParameter("code"));
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readValue(googleJson, JsonNode.class);

            String name = rootNode.findValue("name").asText();
            User found = cormac.getUserRegistry().findByName(name);

            // if user is not found, create a new user 
            if (null == found) {

                // Creting a new user we currently do two non ideal simplifications:
                // the local user name is the "name" attribute from Google, "Joel Ödlund" in my case.
                // We also set a hard coded password. This way a Google user can sign in with his "name" and "password"
                // ideally, we would like to disable conventional login for google users, 
                // and also store the user with the "ID" attribute from google as key
                // Currently, there is no support for this in the backend,
                // and the current solution is to be seen as a proof of concept rather
                // than a deliberate design choice. 

                User u = new User(name, "secret");
                u.setEmail(rootNode.findValue("email").asText());
                u.setFamilyname(rootNode.findValue("family_name").asText());
                u.setGivenname(rootNode.findValue("given_name").asText());

                cormac.getUserRegistry().add(u);
            }

            // User was found or just created, get the local user ID
            long newid = cormac.getUserRegistry().findByName(name).getId();

            // set the session user id in order to pass auth filter
            session.setAttribute("user", newid);

            //clear possible login message
            session.setAttribute("loginMessage", "");

            //redirect to the user page
            response.sendRedirect("user.jsp?user=" + newid);
        }
    }    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>
}
